Création d'API Web avec Python et Flask

Configuration

Conditions préalables

Vous pouvez utiliser les systèmes d'exploitation Windows, macOS ou Linux pour terminer ce didacticiel, et ces quelques instructions qui ne sont pas les mêmes sur toutes les plateformes seront explicitement notées. Python 3, le framework Web Flask et un navigateur Web sont requis pour ce didacticiel, et les instructions d'installation pour toutes les plateformes sont décrites ci-dessous.

La seule connaissance explicitement supposée pour cette leçon est la possibilité d'utiliser un éditeur de texte, tel que BBEdit sur macOS ou Notepad ++ sur Windows. Cependant, la connaissance de la ligne de commande, de Python et des concepts Web tels que HTTP peut rendre ce didacticiel plus facile à suivre. Si vous êtes nouveau dans Python, envisagez de travailler à travers la série Programming Historian sur la gestion des sources en ligne pour vous familiariser avec les concepts fondamentaux de la programmation Python.

Installer Python et Flask

Pour ce tutoriel, vous aurez besoin de Python 3 et du framework Web Flask. Vous aurez également besoin d'un navigateur Web (tel que Firefox) et d'un éditeur de texte (tel que Notepad ++ ou BBEdit).

Pour télécharger Python, suivez ce lien , sélectionnez le bouton qui dit Download Python 3.x.x, puis exécutez le programme d'installation comme vous le feriez normalement pour installer des applications sur votre système d'exploitation. Les paramètres par défaut devraient être corrects.

Pour confirmer que Python a été installé avec succès, ouvrez d'abord la ligne de commande. Sous macOS, cliquez sur l'icône de projecteur dans le coin supérieur droit de votre bureau (la loupe) et tapez terminal. Le terminal doit être la première application qui apparaît. Sous Windows, cliquez sur l'icône du menu Démarrer et tapez cmddans la zone de recherche, puis appuyez sur Enter.

Une fois votre ligne de commande ouverte, entrez ces commandes:

python --version

pip --version

Si la sortie de ces commandes comprend un numéro de version, Python est installé et disponible à partir de la ligne de commande et vous pouvez passer à l'étape suivante.

Ensuite, vous devrez installer Flask. Sur la ligne de commande, tapez

pip install flask

Cela installera Flask à l'aide du gestionnaire de packages pip pour Python. Vous devriez voir une sortie se terminer par une notification indiquant que Flask a été installé avec succès.

Présentation des API

Qu'est-ce qu'une API?

Si vous avez déjà entendu le terme API auparavant, il est probable qu'il ait été utilisé non pas pour faire référence aux API en général, mais plutôt à un type spécifique d'API, l'API Web. Une API Web permet aux informations ou aux fonctionnalités d'être manipulées par d'autres programmes via Internet. Par exemple, avec l'API Web de Twitter, vous pouvez écrire un programme dans un langage comme Python ou Javascript qui peut effectuer des tâches telles que la promotion de tweets ou la collecte de métadonnées de tweet.

Dans la programmation plus généralement, le terme API, abréviation de Application Programming Interface, fait référence à une partie d'un programme informatique conçu pour être utilisé ou manipulé par un autre programme, par opposition à une interface conçue pour être utilisée ou manipulée par un humain. Les programmes informatiques doivent souvent communiquer entre eux ou avec le système d'exploitation sous-jacent, et les API sont une façon de le faire. Dans ce didacticiel, cependant, nous utiliserons le terme API pour faire spécifiquement référence aux API Web.

Quand créer une API

En général, envisagez une API si:

  1. 1.Votre ensemble de données est volumineux, ce qui rend le téléchargement via FTP compliqué ou gourmand en ressources.  

  2. 2.Vos utilisateurs devront accéder à vos données en temps réel, par exemple pour les afficher sur un autre site Web ou dans le cadre d'une application.  

  3. 3.Vos données changent ou sont mises à jour fréquemment.  

  4. 4.Vos utilisateurs n'ont besoin d'accéder à une partie des données à tout moment.  

  5. 5.Vos utilisateurs devront effectuer des actions autres que la récupération de données, telles que la contribution, la mise à jour ou la suppression de données.  

Si vous avez des données que vous souhaitez partager avec le monde, une API est un moyen de les mettre entre les mains des autres. Cependant, les API ne sont pas toujours le meilleur moyen de partager des données avec les utilisateurs. Si la taille des données que vous fournissez est relativement petite, vous pouvez plutôt fournir un «vidage de données» sous la forme d'un fichier JSON, XML, CSV ou SQLite téléchargeable. Selon vos ressources, cette approche peut être viable jusqu'à une taille de téléchargement de quelques gigaoctets.

N'oubliez pas que vous pouvez fournir à la fois un vidage de données et une API, et les utilisateurs individuels peuvent trouver l'un ou l'autre pour mieux correspondre à leur cas d'utilisation. Open Library , par exemple, fournit à la fois un vidage de données et une API , chacune servant à différents cas d'utilisation pour différents utilisateurs.

Terminologie API

Lorsque vous utilisez ou créez des API, vous rencontrerez fréquemment ces termes:

Utilisation d'API

Pourquoi utiliser les API en tant que chercheur?

L'objectif principal de cette leçon est de créer une API, et non d'explorer ou d'utiliser une API qui a déjà été implémentée. Cependant, avant de commencer à créer notre propre API, il peut être utile de discuter de l'utilité des API pour les chercheurs. Dans cette section, nous verrons comment les API peuvent être utiles pour aborder des questions historiques, textuelles ou sociologiques en utilisant une approche «macroscopique» ou «lecture à distance» qui utilise des quantités d'informations relativement importantes. Ce faisant, nous nous familiariserons avec les éléments de base d'une bonne API. La prise en compte des API du point de vue d'un utilisateur sera utile lorsque nous commencerons à concevoir notre propre API plus tard dans la leçon.

Une étude de cas API: sensationnalisme et incendies historiques

Imaginez que notre domaine de recherche soit le sensationnalisme et la presse: la couverture des événements majeurs aux États-Unis est-elle devenue plus ou moins sensationnelle au fil du temps? En rétrécissant le sujet, nous pourrions nous demander si la couverture médiatique, par exemple, des incendies urbains a augmenté ou diminué avec les rapports du gouvernement sur les dépenses de secours liées aux incendies.

Bien que nous ne puissions pas explorer cette question en profondeur, nous pouvons commencer à aborder cet espace de recherche en collectant des données historiques sur la couverture des incendies dans les journaux à l'aide d'une API, dans ce cas, l' API Chronicling America Historical Newspaper . L'API Chronicling America permet d'accéder aux métadonnées et au texte de millions de pages de journaux numérisées. De plus, contrairement à de nombreuses autres API, il ne nécessite pas non plus de processus d'authentification, ce qui nous permet d'explorer immédiatement les données disponibles sans ouvrir de compte.

Notre objectif initial en abordant cette question de recherche est de trouver tous les articles de journaux dans la base de données Chronicling America qui utilisent le terme «feu». En règle générale, l'utilisation d'une API commence par sa documentation. Sur la page API de Chronicling America , nous trouvons deux informations essentielles pour obtenir les données que nous voulons de l'API: l' URL de base de l'API et le chemin correspondant à la fonction que nous voulons exécuter sur l'API - dans ce cas, la recherche dans la base de données .

Notre URL de base est:

http://chroniclingamerica.loc.gov

Toutes les demandes que nous adressons à l'API doivent commencer par cette partie de l'URL. Toutes les API ont une URL de base comme celle-ci qui est la même pour toutes les demandes adressées à l'API.

Notre chemin est:

/search/pages/results/

Si nous combinons l'URL de base et le chemin d'accès ensemble dans une URL, nous aurons créé une demande à l'API Chronicling America qui renvoie toutes les données disponibles dans la base de données:

http://chroniclingamerica.loc.gov/search/pages/results/

Si vous visitez le lien ci-dessus , vous verrez tous les articles disponibles dans Chronicling America (12 243 633 au moment de la rédaction), et pas seulement les entrées liées à notre terme de recherche, «feu». Cette demande renvoie également une vue HTML formatée, plutôt que la vue structurée que nous voulons utiliser pour collecter des données.

Selon la documentation de Chronicling America, afin d'obtenir des données structurées spécifiquement liées au feu, nous devons transmettre un autre type de données dans notre demande: les paramètres de requête .

http://chroniclingamerica.loc.gov/search/pages/results/?format=json&proxtext=fire

Les paramètres de requête suivent le ?dans la demande et sont séparés les uns des autres par le &symbole. Le premier paramètre de requête format=json, change les données renvoyées de HTML en JSON. La seconde, proxtext=fireréduit les entrées renvoyées à celles qui incluent notre terme de recherche.

Si vous suivez le lien ci-dessus dans votre navigateur, vous verrez une liste structurée des éléments de la base de données liés au terme de recherche "feu". Le format des données renvoyées est appelé JSON et est un format structuré qui ressemble à cet extrait des résultats de Chronicling America:

"city": [

        "Washington"

      ],

      "date": "19220730",

      "title": "The Washington Herald.",

      "end_year": 1939,

En effectuant des demandes auprès de l'API Chronicling America, nous avons accédé à des informations sur les actualités contenant le terme de recherche «incendie» et renvoyé des données qui incluent la date de publication et la page sur laquelle l'article apparaît. Si nous devions approfondir cette question de recherche, une prochaine étape pourrait être de trouver combien d'histoires relatives au feu apparaissent sur la première page d'un journal au fil du temps, ou peut-être de nettoyer les données pour réduire le nombre de faux positifs. Comme nous l'avons vu, cependant, l'exploration d'une API peut être une première étape utile dans la collecte de données pour répondre à une question de recherche.

Notez que dans cette section, nous avons sauté une étape importante: trouver une API appropriée en premier lieu. Certaines ressources pour rechercher des API sont disponibles à la fin de cette leçon.

Ce que les utilisateurs veulent dans une API

Comme nous l'avons appris, la documentation est le point de départ d'un utilisateur lorsqu'il travaille avec une nouvelle API, et des URL bien conçues facilitent la recherche intuitive des ressources par les utilisateurs. Parce qu'ils aident les utilisateurs à accéder rapidement aux informations via votre API, ces éléments - documentation et URL bien conçues - sont la condition sine qua non d'une bonne API. Nous aborderons ces éléments plus en détail plus loin dans ce didacticiel.

En utilisant d'autres API dans vos recherches, vous développerez une idée de ce qui fait une bonne API du point de vue d'un utilisateur potentiel. Tout comme les lecteurs chevronnés font souvent de bons écrivains, l'utilisation d'API créées par d'autres et l'évaluation critique de leur mise en œuvre et de leur documentation vous aideront à mieux concevoir vos propres API.

Implémentation de notre API

Aperçu

Cette section vous montrera comment créer un prototype d'API à l'aide de Python et du framework Web Flask. Notre exemple d'API prendra la forme d'une archive de lecture à distance - un catalogue de livres qui va au-delà des informations bibliographiques standard pour inclure des données d'intérêt pour ceux qui travaillent sur des projets numériques. Dans ce cas, outre le titre et la date de publication, notre API servira également la première phrase de chaque livre. Ces données devraient être suffisantes pour nous permettre d'envisager des questions de recherche potentielles sans nous submerger alors que nous nous concentrons sur la conception de notre API.

Nous allons commencer par utiliser Flask pour créer une page d'accueil pour notre site. Dans cette étape, nous apprendrons les bases du fonctionnement de Flask et nous assurerons que notre logiciel est correctement configuré. Une fois que nous aurons une petite application Flask fonctionnant sous la forme d'une page d'accueil, nous effectuerons une itération sur ce site, en la transformant en une API fonctionnelle.

Création d'une application flask

Flask est un cadre Web pour Python, ce qui signifie qu'il fournit des fonctionnalités pour la création d'applications Web, y compris la gestion des requêtes HTTP et des modèles de rendu. Dans cette section, nous allons créer une application Flask de base. Dans les sections ultérieures, nous ajouterons à cette application pour créer notre API. Ne vous inquiétez pas si vous ne comprenez pas encore chaque ligne de code individuelle - des explications seront fournies une fois que cette version initiale de l'application fonctionnera.

Pourquoi Flask?

Python dispose d'un certain nombre de cadres Web qui peuvent être utilisés pour créer des applications Web et des API. Le plus connu est Django, un framework qui a une structure de projet définie et qui comprend de nombreux outils intégrés. Cela peut faire gagner du temps et des efforts aux programmeurs expérimentés, mais peut être écrasant. Les applications Flask ont ​​tendance à être écrites sur une toile vierge, pour ainsi dire, et sont donc plus adaptées à une application confinée telle que notre API prototype.

Tout d'abord, créez un nouveau dossier sur votre ordinateur qui servira de dossier de projet. Cela peut être dans votre Desktopdossier, mais je recommande de créer un projectsdossier dédié pour ce projet et des projets similaires. Ce tutoriel supposera que les fichiers liés à cette leçon seront stockés dans un dossier appelé à l' apiintérieur d'un dossier nommé projectsdans votre répertoire personnel. Si vous avez besoin d'aide pour la navigation sur la ligne de commande, consultez Introduction à la programmation historienne de la ligne de commande Bash pour la ligne de commande macOS et Linux

Sous macOS, vous pouvez créer directement un apidossier dans un projectsdossier de votre répertoire personnel avec cette commande de terminal:

mkdir -p ~/projects/api

Sous Windows, vous pouvez créer le apidossier avec ces commandes dans votre cmdenvironnement de ligne de commande:

md projects

cd projects

md api

Vous pouvez également créer les dossiers projectset à l' apiaide de l'interface utilisateur graphique de votre système d'exploitation.

Ensuite, ouvrez un éditeur de texte (tel que Notepad ++ ou BBEdit) et entrez le code suivant:

import flask

 

app = flask.Flask(__name__)

app.config["DEBUG"] = True

 

 

@app.route('/', methods=['GET'])

def home():

    return "<h1>Distant Reading Archive</h1><p>This site is a prototype API for distant reading of science fiction novels.</p>"

 

app.run()

Enregistrez ce code comme api.pydans le apidossier que vous avez créé pour ce didacticiel.

Exécution de l'application

Dans la ligne de commande, accédez à votre apidossier:

cd projects/api

Vous pouvez vérifier si vous êtes dans le bon dossier en exécutant la pwdcommande. Une fois dans votre répertoire de projet, exécutez l'application Flask avec la commande:

python api.py

Vous devriez voir une sortie similaire à ceci:

 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Vous pouvez également voir quelques lignes liées au débogage. Ce message signifie que Flask exécute votre application localement (sur votre ordinateur) à cette adresse. Suivez le lien ci-dessus, http://127.0.0.1:5000/ , en utilisant votre navigateur Web pour voir l'application en cours d'exécution:

La page d'accueil lorsqu'elle est affichée dans un navigateur.La page d'accueil lorsqu'elle est affichée dans un navigateur. 
 

La page d'accueil lorsqu'elle est affichée dans un navigateur.

Félicitations, vous avez créé une application Web fonctionnelle!

Que fait le flacon

Maintenant que nous avons une page d'accueil pour nos archives, parlons du fonctionnement de Flask et de ce que fait le code ci-dessus.

Flask mappe les requêtes HTTP aux fonctions Python. Dans ce cas, nous avons mappé un chemin URL (' /') à une fonction home,. Lorsque nous nous connectons au serveur Flask à l' adresse http://127.0.0.1:5000/ , Flask vérifie s'il existe une correspondance entre le chemin fourni et une fonction définie. Depuis /, ou aucun chemin supplémentaire fourni, n'a été mappé à la homefonction, Flask exécute le code dans la fonction et affiche le résultat renvoyé dans le navigateur. Dans ce cas, le résultat renvoyé est un balisage HTML pour une page d'accueil accueillant les visiteurs du site hébergeant notre API.

Le processus de mappage des URL aux fonctions est appelé routage . le

@app.route('/', methods=['GET'])

la syntaxe est la partie du programme qui permet à Flask de savoir que cette fonction,, homedoit être mappée sur le chemin /. La methodsliste ( methods=['GET']) est un argument de mot clé qui permet à Flask de savoir quel type de requêtes HTTP sont autorisées. Nous n'utiliserons que les GETdemandes dans ce didacticiel, mais de nombreuses applications Web doivent utiliser à la fois des GETdemandes (pour envoyer des données de l'application à l'utilisateur) et des POSTdemandes (pour recevoir des données d'un utilisateur).

Vous trouverez ci-dessous de brèves explications sur les autres composants de l'application:

import flask - Importe la bibliothèque Flask, rendant le code disponible pour le reste de l'application.

app = flask.Flask(__name__)- Crée l'objet d'application Flask, qui contient des données sur l'application et également des méthodes (fonctions d'objet) qui indiquent à l'application d'effectuer certaines actions. La dernière ligne,, app.run()est une de ces méthodes.

app.config["DEBUG"] = True- Démarre le débogueur. Avec cette ligne, si votre code est mal formé, vous verrez une erreur lorsque vous visiterez votre application. Sinon, vous ne verrez un message générique tel que Bad Gatewaydans le navigateur qu'en cas de problème avec votre code.

app.run() - Une méthode qui exécute le serveur d'applications.

Bien qu'il soit utile de se familiariser avec ce qui se passe dans le script, ne vous inquiétez pas si vous ne comprenez pas précisément ce que fait chaque élément à ce stade. Si vous comprenez les grandes lignes du fonctionnement de cette partie, les détails de la façon dont Flask rend les pages sont susceptibles de devenir plus compréhensibles à mesure que nous continuons à développer notre API.

Création de l'API

Maintenant que nous avons une application Flask en cours d'exécution et que nous savons un peu ce que fait Flask, nous sommes enfin prêts à implémenter une petite API avec des données que nous définirons directement dans notre application.

Nous ajouterons nos données sous forme de liste de dictionnaires Python. Les dictionnaires en Python regroupent des paires de clés et de valeurs, comme ceci:

{

    'key': 'value',

    'key': 'value'

}

La clé identifie le type d'informations représentées, telles que titleou id. La valeur correspond aux données réelles. Par exemple, un petit annuaire téléphonique peut prendre ce format:

[

    {

        'name': 'Alexander Graham Bell',

        'number': '1-333-444-5555'

    },

    {

        'name': 'Thomas A. Watson',

        'number': '1-444-555-6666'

    }

]

L'annuaire ci-dessus est une liste de deux dictionnaires. Chaque dictionnaire est une entrée de l'annuaire téléphonique composée de deux touches nameet numberchacune est associée à une valeur qui fournit les informations réelles.

Ajoutons quelques données (entrées sur trois romans de science-fiction) comme liste de dictionnaires. Chaque dictionnaire contiendra le numéro d'identification, le titre, l'auteur, la première phrase et l'année de publication de chaque livre. Enfin, nous ajouterons une nouvelle fonction: un itinéraire qui permettra à un visiteur d'accéder à nos données.

Remplacez notre code précédent api.pypar le code ci-dessous:

import flask

from flask import request, jsonify

 

app = flask.Flask(__name__)

app.config["DEBUG"] = True

 

# Create some test data for our catalog in the form of a list of dictionaries.

books = [

    {'id': 0,

     'title': 'A Fire Upon the Deep',

     'author': 'Vernor Vinge',

     'first_sentence': 'The coldsleep itself was dreamless.',

     'year_published': '1992'},

    {'id': 1,

     'title': 'The Ones Who Walk Away From Omelas',

     'author': 'Ursula K. Le Guin',

     'first_sentence': 'With a clamor of bells that set the swallows soaring, the Festival of Summer came to the city Omelas, bright-towered by the sea.',

     'published': '1973'},

    {'id': 2,

     'title': 'Dhalgren',

     'author': 'Samuel R. Delany',

     'first_sentence': 'to wound the autumnal city.',

     'published': '1975'}

]

 

 

@app.route('/', methods=['GET'])

def home():

    return '''<h1>Distant Reading Archive</h1>

<p>A prototype API for distant reading of science fiction novels.</p>'''

 

 

# A route to return all of the available entries in our catalog.

@app.route('/api/v1/resources/books/all', methods=['GET'])

def api_all():

    return jsonify(books)

 

app.run()

Exécutez le code (accédez à votre apidossier dans la ligne de commande et entrez python api.py). Une fois le serveur en cours d'exécution, visitez notre URL de route pour afficher les données dans le catalogue:

http://127.0.0.1:5000/api/v1/resources/books/all

Vous devriez voir la sortie JSON pour les trois entrées de notre catalogue de test. Flask nous fournit une jsonifyfonction qui nous permet de convertir des listes et des dictionnaires au format JSON. Dans l'itinéraire que nous avons créé, nos entrées de livre sont converties à partir d'une liste de dictionnaires Python en JSON avant d'être retournées à un utilisateur.

À ce stade, vous avez créé une API fonctionnelle, si elle est limitée. Dans la section suivante, nous allons permettre aux utilisateurs de trouver des livres via des données plus spécifiques, telles que l'ID d'une entrée.

Trouver des ressources spécifiques

À l'heure actuelle, les utilisateurs ne peuvent afficher que l'ensemble de notre base de données - ils ne peuvent pas filtrer ou trouver des ressources spécifiques. Bien que ce ne soit pas un problème avec notre catalogue de tests, cela deviendra rapidement moins utile lorsque nous ajouterons des données. Dans cette section, nous allons ajouter une fonction qui permet aux utilisateurs de filtrer leurs résultats renvoyés à l'aide d'une demande plus spécifique.

Voici le code de notre nouvelle application avec capacité de filtrage. Comme précédemment, nous examinerons le code plus attentivement une fois qu'il sera exécuté.

import flask

from flask import request, jsonify

 

app = flask.Flask(__name__)

app.config["DEBUG"] = True

 

# Create some test data for our catalog in the form of a list of dictionaries.

books = [

    {'id': 0,

     'title': 'A Fire Upon the Deep',

     'author': 'Vernor Vinge',

     'first_sentence': 'The coldsleep itself was dreamless.',

     'year_published': '1992'},

    {'id': 1,

     'title': 'The Ones Who Walk Away From Omelas',

     'author': 'Ursula K. Le Guin',

     'first_sentence': 'With a clamor of bells that set the swallows soaring, the Festival of Summer came to the city Omelas, bright-towered by the sea.',

     'published': '1973'},

    {'id': 2,

     'title': 'Dhalgren',

     'author': 'Samuel R. Delany',

     'first_sentence': 'to wound the autumnal city.',

     'published': '1975'}

]

 

 

@app.route('/', methods=['GET'])

def home():

    return '''<h1>Distant Reading Archive</h1>

<p>A prototype API for distant reading of science fiction novels.</p>'''

 

 

@app.route('/api/v1/resources/books/all', methods=['GET'])

def api_all():

    return jsonify(books)

 

 

@app.route('/api/v1/resources/books', methods=['GET'])

def api_id():

    # Check if an ID was provided as part of the URL.

    # If ID is provided, assign it to a variable.

    # If no ID is provided, display an error in the browser.

    if 'id' in request.args:

        id = int(request.args['id'])

    else:

        return "Error: No id field provided. Please specify an id."

 

    # Create an empty list for our results

    results = []

 

    # Loop through the data and match results that fit the requested ID.

    # IDs are unique, but other fields might return many results

    for book in books:

        if book['id'] == id:

            results.append(book)

 

    # Use the jsonify function from Flask to convert our list of

    # Python dictionaries to the JSON format.

    return jsonify(results)

 

app.run()

Une fois que vous avez mis à jour votre API avec la api_idfonction, exécutez votre code comme auparavant (à python api.pypartir de votre apirépertoire) et visitez les URL ci-dessous pour tester la nouvelle capacité de filtrage:

127.0.0.1:5000/api/v1/resources/books?id=0 127.0.0.1:5000/api/v1/resources/books?id=1 127.0.0.1:5000/api/v1/resources/books?id= 2 127.0.0.1:5000/api/v1/resources/books?id=3

Chacun d'eux doit renvoyer une entrée différente, à l'exception de la dernière, qui doit renvoyer une liste vide:, []car il n'y a pas de livre pour lequel la valeur id est 3. (Le comptage dans la programmation commence généralement à partir de 0, donc id = 3 serait une demande pour un quatrième élément inexistant.) Dans la section suivante, nous explorerons plus en détail notre API mise à jour.

Comprendre notre API mise à jour

Dans ce code, nous créons d'abord une nouvelle fonction, appelée api_id, avec la @app.routesyntaxe qui mappe la fonction au chemin /api/v1/resources/books. Cela signifie que cette fonction s'exécutera lorsque nous accèderons à http://127.0.0.1:5000/api/v1/resources/books . (Notez que l' accès à ce lien sans fournir une carte d' identité donnera le message d'erreur nous avons fourni dans le code: Error: No id field provided. Please specify an id.)

Dans notre fonction, nous faisons deux choses:

Tout d'abord, examinez l'URL fournie pour un identifiant et sélectionnez les livres correspondant à cet identifiant. L'identifiant doit être fourni comme ceci: ?id=0. Les données transmises via des URL comme celle-ci (après le ?) sont appelées paramètres de requête - nous les avons déjà vues lorsque nous avons travaillé avec l'API Chronicling America. C'est une fonctionnalité de HTTP utilisée pour filtrer des types de données spécifiques.

Cette partie du code détermine s'il existe un paramètre de requête, comme ?id=0, puis attribue l'ID fourni à une variable.

    if 'id' in request.args:

        id = int(request.args['id'])

    else:

        return "Error: No id field provided. Please specify an id."

Ensuite, cette section parcourt notre catalogue de livres de test, fait correspondre les livres qui ont l'ID fourni et les ajoute à la liste qui sera retournée à l'utilisateur:

    for book in books:

        if book['id'] == id:

            results.append(book)

Enfin, la return jsonify(results)ligne prend la liste des résultats et les affiche dans le navigateur au format JSON.

Si vous êtes arrivé jusqu'ici, vous avez créé une véritable API. Célébrer! À la fin de cette leçon, vous serez exposé à une API un peu plus complexe qui utilise une base de données, mais la plupart des principes et modèles que nous avons utilisés jusqu'à présent s'appliqueront toujours. Dans la section suivante, nous discuterons de quelques lignes directrices pour créer une API bien conçue que d'autres voudront réellement utiliser. Dans la dernière section du didacticiel, nous appliquerons ces principes à une version de notre API qui extrait les résultats d'une base de données.

Principes de conception des API

Jusqu'à présent, nous avons créé une API fonctionnelle avec des données de test que nous avons fournies directement dans notre application. Notre prochaine version de notre API extraira les données d'une base de données avant de les fournir à un utilisateur. Il faudra également des paramètres de requête supplémentaires, permettant aux utilisateurs de filtrer par des champs autres que l'ID.

Avant d'intégrer davantage de fonctionnalités dans notre application, réfléchissons à certaines des décisions de conception d'API que nous avons prises jusqu'à présent. La convivialité et la maintenabilité sont deux aspects d'une bonne API, et au fur et à mesure que nous intégrerons plus de fonctionnalités à notre API, nous garderons à l'esprit bon nombre des considérations suivantes.

Conception de demandes

La philosophie de conception dominante des API modernes est appelée REST. Pour nos besoins, la chose la plus importante à propos de REST est qu'elle est basée sur les quatre méthodes définies par le protocole HTTP: POST, GET, PUT et DELETE. Celles-ci correspondent aux quatre actions traditionnelles effectuées sur les données d'une base de données: CREATE, READ, UPDATE et DELETE. Dans ce tutoriel, nous ne nous intéresserons qu'aux requêtes GET, qui correspondent à la lecture d'une base de données.

Étant donné que les requêtes HTTP font partie intégrante de l'utilisation d'une API REST, de nombreux principes de conception tournent autour de la façon dont les requêtes doivent être formatées. Nous avons déjà créé une demande HTTP, qui renvoie tous les livres fournis dans nos exemples de données. Pour comprendre les considérations liées au formatage de cette demande, considérons d'abord un exemple faible ou mal conçu de point de terminaison d'API:

http://api.example.com/getbook/10

Le formatage de cette demande présente un certain nombre de problèmes. La première est d'ordre sémantique dans une API REST, nos verbes sont généralement GET, POST, PUTou DELETE, et sont déterminées par la méthode de la demande plutôt que dans l'URL de la requête. Cela signifie que le mot «get» ne devrait pas apparaître dans notre demande, car «get» est impliqué par le fait que nous utilisons une méthode HTTP GET. De plus, les collections de ressources telles que booksou usersdoivent être désignées par des noms pluriels. Cela indique clairement quand une API fait référence à une collection ( books) ou une entrée ( book). En incorporant ces principes, notre API ressemblerait à ceci:

http://api.example.com/books/10

La demande ci-dessus utilise une partie du chemin ( /10) pour fournir l'ID. Bien que ce ne soit pas une approche inhabituelle, elle est quelque peu rigide - avec des URL construites de cette manière, vous ne pouvez généralement filtrer que par un champ à la fois. Les paramètres de requête permettent de filtrer par plusieurs champs de base de données et ont plus de sens lors de la fourniture de données «facultatives», comme un format de sortie:

http://api.example.com/books?author=Ursula+K.+Le Guin&published=1969&output=xml

Lors de la conception de la façon dont les demandes à votre API doivent être structurées, il est également judicieux de planifier les ajouts futurs. Même si la version actuelle de votre API booksne fournit des informations que sur un seul type de ressource , par exemple, il est judicieux de planifier comme si vous pouviez ajouter d'autres ressources ou fonctionnalités hors ressource à votre API à l'avenir:

http://api.example.com/resources/books?id=10

L'ajout d'un segment supplémentaire sur votre chemin, tel que «ressources» ou «entrées», vous donne la possibilité de permettre aux utilisateurs de rechercher parmi toutes les ressources disponibles, ce qui vous permet de faciliter plus tard les demandes d'assistance telles que celles-ci:

https://api.example.com/v1/resources/images?id=10

https://api.example.com/v1/resources/all

Une autre façon de planifier l'avenir de votre API consiste à ajouter un numéro de version au chemin d'accès. Cela signifie que, si vous devez repenser votre API, vous pouvez continuer à prendre en charge l'ancienne version de l'API sous l'ancien numéro de version tout en publiant, par exemple, une deuxième version ( v2) avec des fonctionnalités améliorées ou différentes. De cette façon, les applications et scripts créés à l'aide de l'ancienne version de votre API ne cesseront pas de fonctionner après votre mise à niveau.

Après avoir incorporé ces améliorations de conception, une demande à notre API pourrait ressembler à ceci:

https://api.example.com/v1/resources/books?id=10

Documentation et exemples

Sans documentation, même l'API la mieux conçue sera inutilisable. Votre API doit disposer d'une documentation décrivant les ressources ou les fonctionnalités disponibles via votre API qui fournit également des exemples concrets d'URL de demande ou de code pour votre API. Vous devez avoir une section pour chaque ressource qui décrit les champs, tels que idou title, qu'elle accepte. Chaque section doit avoir un exemple sous la forme d'un exemple de requête HTTP ou d'un bloc de code.

Une pratique assez courante dans la documentation des API consiste à fournir des annotations dans votre code qui sont ensuite automatiquement rassemblées dans la documentation à l'aide d'un outil tel que Doxygen ou Sphinx . Ces outils créent de la documentation à partir de docstrings - des commentaires que vous faites sur vos définitions de fonction. Bien que ce type de documentation soit une bonne idée, vous ne devriez pas considérer votre travail comme terminé si vous n'avez documenté votre API qu'à ce niveau. Au lieu de cela, essayez de vous imaginer en tant qu'utilisateur potentiel de votre API et fournissez des exemples pratiques. Dans un monde idéal, vous auriez trois types de documentation pour votre API: une référence qui détaille chaque route et son comportement, un guide qui explique la référence en prose, et au moins un ou deux tutoriels qui expliquent chaque étape en détail.

Pour vous inspirer sur la façon d'aborder la documentation de l'API, consultez l' API des collections numériques de la Bibliothèque publique de New York , qui définit une norme de documentation réalisable pour de nombreux projets universitaires. Pour une API largement documentée (bien que parfois écrasante), voir l' API MediaWiki Action , qui fournit de la documentation aux utilisateurs qui transmettent des requêtes partielles à l'API. (Dans notre exemple ci-dessus, nous avons renvoyé une erreur sur une requête partielle.) Pour d'autres exemples de documentation d'API gérés par des professionnels, considérez l' API de la Banque mondiale , les différentes API du New York Times ou l' API Europeana Pro .

Connecter notre API à une base de données

Ce dernier exemple de notre API Distant Reading Archive extrait les données d'une base de données, implémente la gestion des erreurs et peut filtrer les livres par date de publication. La base de données utilisée est SQLite, un moteur de base de données léger qui est pris en charge en Python par défaut. Les fichiers SQLite se terminent généralement par l' .dbextension de fichier.

Avant de modifier notre code, téléchargez d' abord la base de données d'exemple à partir de cet emplacement et copiez le fichier dans votre apidossier à l'aide de votre interface utilisateur graphique. La version finale de notre API interrogera cette base de données lors du retour des résultats aux utilisateurs.

Copiez le code ci-dessous dans votre éditeur de texte. Comme précédemment, nous examinerons le code de plus près une fois qu'il sera exécuté.

import flask

from flask import request, jsonify

import sqlite3

 

app = flask.Flask(__name__)

app.config["DEBUG"] = True

 

def dict_factory(cursor, row):

    d = {}

    for idx, col in enumerate(cursor.description):

        d[col[0]] = row[idx]

    return d

 

 

@app.route('/', methods=['GET'])

def home():

    return '''<h1>Distant Reading Archive</h1>

<p>A prototype API for distant reading of science fiction novels.</p>'''

 

 

@app.route('/api/v1/resources/books/all', methods=['GET'])

def api_all():

    conn = sqlite3.connect('books.db')

    conn.row_factory = dict_factory

    cur = conn.cursor()

    all_books = cur.execute('SELECT * FROM books;').fetchall()

 

    return jsonify(all_books)

 

 

 

@app.errorhandler(404)

def page_not_found(e):

    return "<h1>404</h1><p>The resource could not be found.</p>", 404

 

 

@app.route('/api/v1/resources/books', methods=['GET'])

def api_filter():

    query_parameters = request.args

 

    id = query_parameters.get('id')

    published = query_parameters.get('published')

    author = query_parameters.get('author')

 

    query = "SELECT * FROM books WHERE"

    to_filter = []

 

    if id:

        query += ' id=? AND'

        to_filter.append(id)

    if published:

        query += ' published=? AND'

        to_filter.append(published)

    if author:

        query += ' author=? AND'

        to_filter.append(author)

    if not (id or published or author):

        return page_not_found(404)

 

    query = query[:-4] + ';'

 

    conn = sqlite3.connect('books.db')

    conn.row_factory = dict_factory

    cur = conn.cursor()

 

    results = cur.execute(query, to_filter).fetchall()

 

    return jsonify(results)

 

app.run()

Enregistrez le code comme api_final.pydans votre apidossier et exécutez-le en accédant à votre dossier de projet dans le terminal et en entrant la commande:

python api_final.py

Notez que si une version précédente du code est toujours en cours d'exécution, vous devrez d'abord terminer ce processus en appuyant sur Control-Cavant d'exécuter le nouveau code. Une fois cet exemple exécuté, essayez la fonctionnalité de filtrage avec ces requêtes HTTP:

http://127.0.0.1:5000/api/v1/resources/books/all http://127.0.0.1:5000/api/v1/resources/books?author=Connie+Willis http://127.0.0.1: 5000 / api / v1 / resources / books? Author = Connie + Willis & published = 1999 http://127.0.0.1:5000/api/v1/resources/books?published=2010

La base de données téléchargée pour cette leçon contient 67 entrées, une pour chacun des lauréats du Prix Hugo du meilleur roman de science-fiction entre 1953 et 2014 (en évitant la controverse de vote de 2015). L'ensemble de données comprend le titre du roman, l'auteur, l'année de publication et la première phrase. Notre API permet aux utilisateurs de filtrer par trois champs: id, published(année de publication), et author.

La première demande renvoie toutes les entrées de la base de données, similaire à la /alldemande que nous avons implémentée pour la dernière version de notre API. La deuxième demande renvoie tous les livres de l'auteur Connie Willis ( ?author=Connie+Willis). Notez que, dans un paramètre de requête, les espaces entre les mots sont désignés par un +signe, donc Connie+Willis. La troisième demande est filtrée par deux champs: l'auteur et l'année de publication. Au lieu des trois livres retournés en faisant la demande ?author=Connie+Willis, cette demande renvoie uniquement l'entrée du Doomsday Book , publiée en 1993. La dernière demande renvoie tous les gagnants d'Hugo de l'année 2010 (notez que, certaines années, plus d'un Hugo est attribué) .

Comme nous pouvons le voir, cette version de notre API sert un plus grand nombre de résultats, résultats qui sont stockés dans une base de données SQLite ( books.db). Lorsque notre utilisateur demande une entrée ou un ensemble d'entrées, notre API extrait ces informations de la base de données en créant et en exécutant une requête SQL. Cette itération de notre API permet également de filtrer par plus d'un champ. Nous discuterons des utilisations potentielles de cette fonctionnalité après avoir examiné notre code de plus près.

Comprendre notre API basée sur une base de données

Les bases de données relationnelles permettent le stockage et la récupération des données, qui sont stockées dans des tables. Les tableaux sont similaires aux feuilles de calcul dans la mesure où ils comportent des colonnes et des lignes - les colonnes indiquent ce que les données représentent, telles que «titre» ou «date». Les lignes représentent des entrées individuelles, qui peuvent être des livres, des utilisateurs, des transactions ou tout autre type d'entité.

La base de données nous travaillons avec a cinq colonnes id, published, author, titleet first_sentence. Chaque ligne représente un livre qui a remporté le prix Hugo dans l'année sous le publishedtitre, et dont le texte commence par la phrase dans la first_sentencecolonne.

Plutôt que d'utiliser des données de test définies dans l'application, notre api_allfonction extrait les données de notre base de données Hugo:

def api_all():

    conn = sqlite3.connect('books.db')

    conn.row_factory = dict_factory

    cur = conn.cursor()

    all_books = cur.execute('SELECT * FROM books;').fetchall()

 

    return jsonify(all_books)

Tout d'abord, nous nous connectons à la base de données à l'aide de notre sqlite3bibliothèque. Un objet représentant la connexion à la base de données est lié à la connvariable. La conn.row_factory = dict_factoryligne permet à l'objet de connexion de savoir utiliser la dict_factoryfonction que nous avons définie, qui renvoie les éléments de la base de données sous forme de dictionnaires plutôt que de listes - ceux-ci fonctionnent mieux lorsque nous les exportons vers JSON. Nous créons ensuite un objet curseur ( cur = conn.cursor()), qui est l'objet qui se déplace réellement dans la base de données pour extraire nos données. Enfin, nous exécutons une requête SQL avec la cur.executeméthode pour extraire toutes les données disponibles ( *) de la bookstable de notre base de données. A la fin de notre fonction, ces données sont retournées comme JSON: jsonify(all_books). Notez que notre autre fonction qui renvoie des données,api_filter, utilisera une approche similaire pour extraire les données de la base de données.

Le but de notre page_not_foundfonction est de créer une page d'erreur vue par l'utilisateur si l'utilisateur rencontre une erreur ou entre un itinéraire qui n'a pas été défini:

@app.errorhandler(404)

def page_not_found(e):

    return "<h1>404</h1><p>The resource could not be found.</p>", 404

Dans les réponses HTML, le code 200signifie «OK» (les données attendues transférées), tandis que le code 404signifie «Introuvable» (aucune ressource n'était disponible à l'URL indiquée). Cette fonction nous permet de renvoyer 404 pages en cas de problème dans l'application.

Notre api_filterfonction est une amélioration de notre api_idfonction précédente qui renvoie un livre en fonction de son ID. Cette nouvelle fonction permet de filtrer par trois domaines différents: id, publishedet author. La fonction saisit d'abord tous les paramètres de requête fournis dans l'URL (rappelez-vous, les paramètres de requête sont la partie de l'URL qui suit ?, comme ?id=10).

query_parameters = request.args

Il tire ensuite les paramètres pris en charge id, publishedet authoret de les lier à des variables appropriées:

id = query_parameters.get('id')

published = query_parameters.get('published')

author = query_parameters.get('author')

Le segment suivant commence à créer une requête SQL qui sera utilisée pour trouver les informations demandées dans la base de données. Les requêtes SQL utilisées pour rechercher des données dans une base de données prennent cette forme:

`SELECT <columns> FROM <table> WHERE <column=match> AND <column=match>;

Pour obtenir les données correctes, nous devons créer à la fois une requête SQL qui ressemble à ce qui précède et une liste avec les filtres qui seront mis en correspondance. Combinés, la requête et les filtres fournis par l'utilisateur nous permettront d'extraire les bons livres de notre base de données.

Nous commençons à définir à la fois la requête et la liste de filtres:

query = "SELECT * FROM books WHERE"

to_filter = []

Ensuite, si id,, publishedou authoront été fournis en tant que paramètres de requête, nous les ajoutons à la fois à la requête et à la liste de filtres:

    if id:

        query += ' id=? AND'

        to_filter.append(id)

    if published:

        query += ' published=? AND'

        to_filter.append(published)

    if author:

        query += ' author=? AND'

        to_filter.append(author)

Si l'utilisateur n'a fourni aucun de ces paramètres de requête, nous n'avons rien à montrer, nous les envoyons donc à la page "404 Not Found":

    if not (id or published or author):

        return page_not_found(404)

Pour perfectionner notre requête, nous supprimons le `AND and cap the query with the ;` de fin requis à la fin de toutes les instructions SQL:

    query = query[:-4] + ';'

Enfin, nous nous connectons à notre base de données comme dans notre api_allfonction, puis exécutons la requête que nous avons construite à l'aide de notre liste de filtres:

conn = sqlite3.connect('books.db')

conn.row_factory = dict_factory

cur = conn.cursor()

 

results = cur.execute(query, to_filter).fetchall()

Enfin, nous renvoyons les résultats de notre requête SQL exécutée en JSON à l'utilisateur:

return jsonify(results)

Ouf! Quand tout est dit et fait, cette section de code lit les paramètres de requête fournis par l'utilisateur, crée une requête SQL basée sur ces paramètres, exécute cette requête pour trouver les livres correspondants dans la base de données et renvoie ces correspondances sous forme de JSON à l'utilisateur. Cette section de code rend la capacité de filtrage de notre API considérablement plus sophistiquée: les utilisateurs peuvent désormais trouver des livres, par exemple, d'Ursula K. Le Guin qui ont été publiés en 1975 ou tous les livres de la base de données publiée en 2010.

Notre API en pratique

Maintenant que nous avons implémenté notre API de lecture à distance, considérons comment elle pourrait être utile dans des projets numériques et dans la recherche.

L'un des avantages de la fourniture de données via une API, par opposition à la fourniture d'une sorte de base de données ou fichier téléchargeable pour les utilisateurs, est que, à mesure que de nouvelles données ou ressources supplémentaires sont ajoutées, elles deviennent immédiatement disponibles pour les projets créés à l'aide de l'API. Imaginez que nous rendions notre API accessible au public, et qu'un utilisateur crée une visualisation qui trace la longueur de la première phrase d'un roman en caractères par rapport à son année de publication:

Diagramme de dispersion de la longueur de la première phrase par rapport à la date de publication.Diagramme de dispersion de la longueur de la première phrase par rapport à la date de publication. 
 

Diagramme de dispersion de la longueur de la première phrase par rapport à la date de publication.

Lorsque de nouveaux gagnants Hugo ont été ajoutés à la base de données, le script qui a généré cette visualisation pourrait immédiatement utiliser les nouvelles informations. Si la visualisation était créée dans D3 ou dans un autre utilitaire Web, ce tracé refléterait en fait des données supplémentaires ajoutées à l'archive du livre dès que l'archive était mise à jour, c'est-à-dire en temps réel. Au fur et à mesure que des données supplémentaires s'accumulaient, nous pourrions, par exemple, savoir si l'ouverture inhabituellement longue de John Scalzi à ses Chemises rouges 2013 était une aberration ou la poursuite d'une tendance plus longue vers la verbosité dans la science-fiction. Inversement, si votre API devait changer sa structure d'URL ou cesser de fonctionner, les applications basées sur elle ne fonctionneraient plus. N'oubliez pas que lors de la création d'une API, vous assumez une certaine responsabilité pour les applications que d'autres peuvent créer avec.

Une API solide peut être considérée comme l'épine dorsale d'un nombre potentiellement illimité de projets ou de pistes de recherche. Bien que l'exemple ci-dessus prenne la forme d'une visualisation de la quantité limitée de données que nous avons fournies dans nos archives de lecture à distance, un projet basé sur cette API pourrait tout aussi facilement prendre la forme d'un Twitterbot qui partage les premières phrases (apprenez à faire un avec çaProgrammation Historian) ou une page Web de bibliothèque qui affiche les ouvertures de livres et l'année de publication aux côtés d'autres métadonnées de livres. Dans de nombreux cas, il est judicieux de créer d'abord une interface API pour vos données ou fonctionnalités principales avant de les extrapoler pour créer une visualisation, une application ou un site Web. Non seulement cela rend votre travail accessible aux chercheurs travaillant sur d'autres projets, mais cela conduit souvent à un projet plus compréhensible et plus facile à gérer.

Ressources

Les ressources ci-dessous fournissent des informations sur les API utiles pour les chercheurs en sciences humaines et sociales ainsi que des lectures supplémentaires sur les concepts des API.

API pour les chercheurs en sciences humaines

Chronicling America (Library Of Congress) - A digitized collection of American newspaper articles from the 18th to the 20th century.

Connecting Repositories (CORE) - A collection of open access articles from various sources hosted by the Open University.

English Broadside Ballad Archive (EBBA)

History Data Service (HDS) - A collection of data from a wide variety of historical sources.

Europeana

Digging into Data API List

Ressources API

Anatomy of a URL - Explains the different sections of a URL (protocol, domain, path, and so on) in greater detail.

Original Paper on REST - PhD thesis by Roy Thomas Fielding that introduced the concepts behind the REST philosophy of API design.

The Flask Mega Tutorial - The most well-known tutorial for learning the Flask web framework.